package com.taotao.tools.spring.transaction;

import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * <p>
 * 用编程式事务实现子线程和主线程事务的同时回滚
 * <pre>
 *     <code>
         @Resource
         private TransactionTemplate transactionTemplate;

         ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 5, 5, TimeUnit.MINUTES, new ArrayBlockingQueue<>(10), new ThreadPoolExecutor.CallerRunsPolicy());
         new MultiTransactionExecutor(transactionTemplate, threadPoolExecutor).runMultiThreadTransaction(new Runnable() {
            @Override
            public void run() {
            // 主线程自己事务，自己回滚
            SysUser u = new SysUser();
            u.setUserName("fdsfs32422424");
            EntityUtils.setPo5Field(1L, LocalDateTime.now(), u);
            sysUserService.saveIgnoreNull(u);
            throw new RuntimeException("Exception main");
            }
        }, new Runnable() {
            @Override
            public void run() {
            SysFunc func = new SysFunc();
            func.setCode("234");
            EntityUtils.setPo5Field(1L, LocalDateTime.now(), func);
            sysFuncMapper.saveIgnoreNull(func);
            }
        }, new Runnable() {
            @Override
            public void run() {
            System.out.println("Process 2 without exception");
            // 子线程自己控制事务
            SysRole r = new SysRole();
            r.setRoleName("324242");
            EntityUtils.setPo5Field(1L, LocalDateTime.now(), r);
            sysRoleMapper.saveIgnoreNull(r);
            }
        });
 *
 *
 *     </code>
 * </pre>
 * </p>
 *
 * @author youbeiwuhuan
 * @version 1.0.0
 * @date 2023/7/10 15:49
 */
public class MultiTransactionExecutor {
    private final TransactionTemplate transactionTemplate;

    private final ThreadPoolExecutor threadPool;

    private volatile boolean hasException = false;
    private volatile boolean hasUsed = false;

    public class TransactionRunable implements Runnable {
        private boolean hasCountDown = false;

        private Runnable runnable;

        private CountDownLatch latch;

        private PlatformTransactionManager transactionManager;

        public TransactionRunable(Runnable runnable, CountDownLatch latch, PlatformTransactionManager transactionManager) {
            this.runnable = runnable;
            this.latch = latch;
            this.transactionManager = transactionManager;
        }

        @Override
        public void run() {
            DefaultTransactionDefinition def = new DefaultTransactionDefinition();

            //设置事务的传播性，默认使用当前，如果没有则创建
            def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
            TransactionStatus status = transactionManager.getTransaction(def);


            try {

                runnable.run();

                latch.countDown();
                hasCountDown = true;
                // 等待所有子线程执行完毕一起确定是提交还是回滚
                latch.await();

                if (hasException()) {
                    transactionManager.rollback(status);
                } else {
                    transactionManager.commit(status);
                }
            } catch (Exception e) {
                setHasException(true);
                transactionManager.rollback(status);
                throw new RuntimeException(e);
            } finally {
                if (!hasCountDown) {
                    latch.countDown();
                }
            }

        }

        protected void setHasException(boolean b) {
            hasException = b;
        }

        protected boolean hasException() {
            return hasException;
        }
    }


    public MultiTransactionExecutor(TransactionTemplate transactionTemplate, ThreadPoolExecutor threadPool) {
        this.transactionTemplate = transactionTemplate;
        this.threadPool = threadPool;
    }


    private synchronized void checkUsed() {
        if (hasUsed) {
            throw new IllegalStateException("MultiTransactionExecutor 只能使用一次！不能重复使用。");
        }

        hasUsed = true;
    }


    /**
     * 并行执行事务，遇到一个异常则都回滚,抛出第一个异常。注意 主线程与分支线程事务之间、各个分支线程之间不要产生锁竞争(不要同时操作同一个数据)，否则容易造成死锁或锁超时。
     *
     * @param main 主线程事务逻辑
     * @param subs 分支线程事务逻辑
     * @throws Exception
     */
    public void runMultiThreadTransaction(final Runnable main, final Runnable... subs) throws Exception {
        if (subs == null || subs.length == 0) {
            throw new IllegalArgumentException();
        }

        checkUsed();


        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        //设置事务的隔离性，默认数据库的事务隔离性
//        def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);

        //设置事务的传播性
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        PlatformTransactionManager transactionManager = transactionTemplate.getTransactionManager();
        TransactionStatus status = transactionManager.getTransaction(def);

        try {

            // 主线程自己事务，自己回滚
            if (null != main) {
                main.run();
            }

            CountDownLatch latch = new CountDownLatch(subs.length);
            CompletableFuture<Void>[] futures = new CompletableFuture[subs.length];
            for (int i = 0; i < subs.length; i++) {
                CompletableFuture<Void> future = CompletableFuture.runAsync(new TransactionRunable(subs[i], latch, transactionTemplate.getTransactionManager()), threadPool);
                futures[i] = future;
            }

            // 这里获取异常
            CompletableFuture.allOf(futures).get();

            // 等待所有子线程执行完毕
            latch.await();
            transactionManager.commit(status);
        } catch (Exception e) {
            hasException = true;
            // 事务回滚
            transactionManager.rollback(status);
            throw e;

        }


    }
}
